#include "ModelEntity.h"
#include "core/CMath.h"
#include "GLTFParser.h"
#include "Cache.h"
#include "Global.h"

void ModelEntity::create(ModelType type, int ID) {
	int meshNum = GLTFParser::meshNum(type);
	if (meshNum == 1) {
		fillOneMesh(type);
		return;
	}
	for (int i = 0; i < meshNum; i++)// multi-mesh model
		fillOneMesh(type, i, true);
}

/** @note condition exists: ID keep the same, but modelType change! */
void ModelEntity::updateState() {
	const auto& arg = Cache::modelArg(m_ID);
	if (Proj::isSSQK()) {
		setPosition(Vec3(arg.pos.x, Config::Y_Model, arg.pos.z));

		const auto& scalerFunc = DataFilter::get()->get2DScalerFunc();
		auto scale = scalerFunc(arg.pos.z);
		setScale(scale);

		bool hasMixColor = arg.recog == 2;
		addUniform("mixColorEnabled", hasMixColor);
		if (hasMixColor)
			setMaterialColor("Orange");
	} else
		setPosition(Vec3(arg.pos.x, Config::Y_Model, arg.pos.z));

	float rotAngle = arg.angle;
	if (arg.isOncoming) rotAngle += C_PI;
	setRotationY(rotAngle);

	if (Proj::isSSQK())
		return;
	
	if (GLTFParser::isTrafficBarrier(arg.type))
		disableLight();
	else if (arg.color.isValid()) {// has pure color: isAccTracking/FCW/PCW/(SQ)ACAS/(SQ)ALC
		setMaterialColor(arg.color);
		/** PROJECT SQ: disable light, make the model's color pure -- do not be affected by the light color*/
		disableLight();
		m_hasNewMatColor = true;
	} else if (m_hasNewMatColor) {// for the same model: last has new color, now hasn't => reset to default!
		setMaterialColor(GLTFParser::material(arg.type).baseColor);// reset to default glTF color
		enableLight();
		m_hasNewMatColor = false;
	}

	if (Proj::isKT() || Proj::isSQZK()) {
		const auto& ObjInfo = GLTFParser::info(arg.type);
		auto sx = arg.width / ObjInfo.width;
		auto sy = sx;
		auto sz = arg.length / ObjInfo.length;
		setScale(sx, sy, sz);

		const auto& children = Entity::children();
		if (children.empty())
			attachEllipseEffect(this);
		else {
			for (auto child : children)
				attachEllipseEffect(child);
		}
	}
}

void ModelEntity::fillOneMesh(ModelType type, int meshIndex, bool asChild) {
	auto e = this;
	if (asChild) e = new ModelEntity(this);
	e->setMeshProperty(meshIndex, GLTFParser::meshName(type, meshIndex));

	const auto& material = GLTFParser::material(type, meshIndex);
	e->setMaterial(material);

	bool isZhiche = ModelType::Zhiche == type;
	e->addUniform("isZhiche", isZhiche);
	if (isZhiche)
		e->disableFadeOutEdge();

	if (material.hasTexture())
		e->setTexcoords(GLTFParser::texcoords(type, meshIndex));
	e->setVertices(GLTFParser::vertices(type, meshIndex));
	e->setIndices(GLTFParser::indices(type, meshIndex));

	if (Proj::isSSQK())
		e->install(Config::Shader_ModelTexture);
	else {
		e->addUniform("pbrGlobalAmbientCompnt", Config::Material_PBRGlobalAmbientCompnt);
		e->setLightEnabled(true);
		e->setNormals(GLTFParser::normals(type, meshIndex));
		e->setTransform(GLTFParser::transform(type, meshIndex));
		e->install(Config::Shader_ModelPBR);
	}
}

void ModelEntity::attachEllipseEffect(Entity* e) {
	bool isALCReady = Cache::isALCReady();
	bool isLDWReady = Cache::isLDWReady();
	bool isEllipseActive = Global::isBlinkingOn() && (isALCReady || isLDWReady);

	const char* Ellipse = "ellipse";
	e->addUniformStruct(Ellipse, Field("isActive", isEllipseActive));
	if (!isEllipseActive)
		return;

	e->addUniformStruct(Ellipse,
		Field("centerZ", Config::Marker_Ellipse_CenterZ),
		Field("hAxis", Config::Marker_Ellipse_HAxis),
		Field("vAxis", Config::Marker_Ellipse_VAxis),
		Field("isALCActive", false),
		Field("isALCToLeft", false),
		Field("colorALC", CColor()),
		Field("isLDWActive", false),
		Field("isLDWFromLeft", false),
		Field("colorLDW", Config::Marker_Ellipse_ColorWarning)
	);

	if (isALCReady) {
		e->addUniformStruct(Ellipse, Field("isALCActive", true));
		auto alc = Cache::status().alc;
		switch (alc) {
			case ALC::Left_Normal:
				e->addUniformStruct(Ellipse,
					Field("isALCToLeft", true),
					Field("colorALC", Config::Marker_Ellipse_ColorNormal)
				);
				break;
			case ALC::Left_Warning:
				e->addUniformStruct(Ellipse,
					Field("isALCToLeft", true),
					Field("colorALC", Config::Marker_Ellipse_ColorWarning)
				);
				break;
			case ALC::Right_Normal:
				e->addUniformStruct(Ellipse,
					Field("isALCToLeft", false),
					Field("colorALC", Config::Marker_Ellipse_ColorNormal)
				);
				break;
			case ALC::Right_Warning:
				e->addUniformStruct(Ellipse,
					Field("isALCToLeft", false),
					Field("colorALC", Config::Marker_Ellipse_ColorWarning)
				);
				break;
		}
	}

	if (isLDWReady) {
		e->addUniformStruct(Ellipse, Field("isLDWActive", true));
		auto ldw = Cache::status().ldw;
		switch (ldw) {
			case LDW::From_Left_Warning:
				e->addUniformStruct(Ellipse, Field("isLDWFromLeft", true));
				break;
			case LDW::From_Right_Warning:
				e->addUniformStruct(Ellipse, Field("isLDWFromLeft", false));
				break;
		}
	}

	CVector<float> CArr, CArrR;
	CArr << 0.0 << 0.0 << 0.0 << 0.0;
	CArrR = CArr;
	if (isALCReady || isLDWReady) {
		const auto& lineArg = Cache::lineArg(LineTag::L);
		const auto& lineArgR = Cache::lineArg(LineTag::R);
		if (lineArg.isValid()) CArr = lineArg.eq.C;
		if (lineArgR.isValid()) CArrR = lineArgR.eq.C;
	}
	e->addUniformArray("eqC", CArr);
	e->addUniformArray("eqRC", CArrR);
}